
#include <render/enviroment/camera.h>

#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>

Camera::Camera ( glm::vec3 position, double fieldOfView, double aspect, double zNear, double zFar ) {
    
    this -> mPosition = position;
    this -> mForward = glm::vec3 ( 0.0, 0.0, 1.0 );
    this -> mUp = glm::vec3 ( 0.0, 1.0, 0.0 );
    
    this -> fieldOfView = fieldOfView;
    this -> aspect = aspect;
    this -> zNear = zNear;
    this -> zFar = zFar;

    this -> yaw = 90.0;
    this -> pitch = 0.0;
    
    this -> applyChanges ();
}

void Camera::setPosition ( glm::vec3 position ) {

    this -> mPosition = position;
}

void Camera::setCameraTarget ( glm::vec3 target ) {
    /*
     * вычитание делаем, чтобы сделать z координату положительной,
     * поскольку по соглашению openGL камера указывает вдоль отрицательной оси z
    */
    this -> mDirection = glm::normalize ( this -> mPosition - target );

    glm::vec3 up = glm::vec3 ( 0.0f, 1.0f, 0.0f );
    this -> mRight = glm::normalize ( glm::cross ( up, this -> mDirection ) );

    this -> mUp = glm::cross ( this -> mDirection, this -> mRight );
}

void Camera::setCameraTarget ( Amouse* mouse ) {

    this -> yaw += mouse -> offsetX;
    this -> pitch += mouse -> offsetY;


    if ( this -> pitch > 89.0 ) {

        this -> pitch = 89.0;

    } else if ( this -> pitch < -89.0 ) {

        this -> pitch = -89.0;
    }


    glm::vec3 front;

    front.x = cos ( glm::radians ( this -> yaw ) ) * cos ( glm::radians ( this -> pitch ) );
    front.y = sin ( glm::radians ( this -> pitch ) );
    front.z = sin ( glm::radians ( this -> yaw ) ) * cos ( glm::radians ( this -> pitch ) );

    this -> mForward = glm::normalize ( front );
}



void Camera::setFieldOfView ( double FOV ) {

    this -> fieldOfView = FOV;
}

void Camera::setAspect ( double aspect ) {

    this -> aspect = aspect;
}

void Camera::setNearClipping ( double zNear ) {

    this -> zNear = zNear;
}

void Camera::setFarClipping ( double zFar ) {

    this -> zFar = zFar;
}


glm::vec3 Camera::getPosition () {

    return this -> mPosition;
}

glm::vec3 Camera::getCameraTarget () {

    return this -> mDirection;
}


double Camera::getFieldOfView () {

    return this -> fieldOfView;
}

double Camera::getAspect () {

    return this -> aspect;
}

double Camera::getNearClipping () {

    return this -> zNear;
}

double Camera::getFarClipping () {

    return this -> zFar;
}


glm::mat4 Camera::getViewProjection () {

    return mProjection * mView;
}

void Camera::applyChanges () {

    this -> mProjection = glm::perspective ( fieldOfView, aspect, zNear, zFar );

    this -> mView = glm::lookAt ( this -> mPosition,
                                        this -> mPosition + this -> mForward,
                                        this -> mUp );
}
